﻿using Sprout.Core.Paging;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Threading.Tasks;

namespace Sprout.Repository.Repository
{
    public class RepositoryFacotry
    {
        public static Dictionary<string, MethodInfo> MethodsCache { get; set; } = new Dictionary<string, MethodInfo>();
        public static ConcurrentDictionary<string, Type> TargetTypeCache = new ConcurrentDictionary<string, Type>();

        /// <summary>
        /// 构建生成对象
        /// </summary>
        /// <param name="interfaceType"></param>
        /// <returns></returns>
        public static object BuildObject(Type interfaceType)
        {
            var resultType = BuildType(interfaceType);
            var result = Activator.CreateInstance(resultType);
            return result;
        }
        /// <summary>
        /// 构建成类型
        /// </summary>
        /// <param name="interfaceType"></param>
        /// <returns></returns>
        public static Type BuildType(Type interfaceType)
        {
            var cacheKey = interfaceType.FullName;
            var resultType = TargetTypeCache.GetOrAdd(cacheKey, (s) => CreateType(interfaceType));
            return resultType;
        }
        /// <summary>
        /// 创建类型
        /// </summary>
        /// <param name="interfaceType"></param>
        /// <returns></returns>
        public static Type CreateType(Type interfaceType)
        {
            string assemblyName = $"{interfaceType.Assembly.GetName().Name}.ProxyAssembly";
            string moduleName = $"{assemblyName}.{interfaceType.Name}ProxyModule";
            string typeName = $"{assemblyName}.{interfaceType.Name}ProxyService";
            //程序集
            AssemblyName assyName = new AssemblyName(assemblyName);
            //程序集构建方式
            AssemblyBuilder assyBuilder = AssemblyBuilder.DefineDynamicAssembly(assyName, AssemblyBuilderAccess.Run);
            //模块名称
            ModuleBuilder modBuilder = assyBuilder.DefineDynamicModule(moduleName);
            //父类型
            Type parentType = typeof(object);
            //要实现的接口
            Type[] interfaceTypes = new Type[] { interfaceType };
            //新类型的属性
            TypeAttributes newTypeAttribute = TypeAttributes.Class | TypeAttributes.Public;
            //得到类型生成器            
            TypeBuilder typeBuilder = modBuilder.DefineType(typeName, newTypeAttribute, parentType, interfaceTypes);

            //获取所有待实现的接口
            var allInterfaces = interfaceType.GetInterfaces();
            //需要实现的方法
            List<MethodInfo> targetMethods = new List<MethodInfo>();
            targetMethods.AddRange(interfaceType.GetMethods());
            foreach (var iInterface in allInterfaces)
            {
                targetMethods.AddRange(iInterface.GetMethods());
            }

            var repositoryType = typeof(RepositoryService);

            foreach (MethodInfo targetMethod in targetMethods)
            {
                //只挑出virtual的方法
                if (targetMethod.IsVirtual)
                {
                    //缓存接口的方法体，便于后续将方法体传递给service
                    string methodKey = Guid.NewGuid().ToString();
                    MethodsCache[methodKey] = targetMethod;

                    //方法名
                    var methodName = targetMethod.Name;
                    //方法参数
                    var paramInfos = targetMethod.GetParameters();
                    var parameterType = paramInfos.Select(it => it.ParameterType).ToArray();
                    //返回值
                    var returnType = targetMethod.ReturnType;

                    //通过emit生成方法体
                    MethodBuilder methodBuilder = typeBuilder.DefineMethod(targetMethod.Name,
                        MethodAttributes.Public | MethodAttributes.Virtual,
                        targetMethod.ReturnType,        //返回类型
                        parameterType);     //参数的类型

                    //获取IL生成器
                    ILGenerator il = methodBuilder.GetILGenerator();

                    var isTask = typeof(Task) == returnType;
                    var isVoid = typeof(void) == returnType;
                    var isAsync = typeof(Task).IsAssignableFrom(returnType) && !isTask;
                    var hasReturnValue = !isTask && !isVoid;

                    var selectAttribute = targetMethod.GetCustomAttribute<SelectAttribute>();
                    var executeAttribute = targetMethod.GetCustomAttribute<ExecuteAttribute>();
                    if (selectAttribute == null && executeAttribute == null)
                    {
                        throw new Exception($"方法：{interfaceType.FullName}.{targetMethod.Name}必须具有特性SelectAttribute或ExecuteAttribute");
                    }
                    var attributeNumber = 0;
                    if (selectAttribute != null) { attributeNumber++; }
                    if (executeAttribute != null) { attributeNumber++; }
                    if (attributeNumber > 1)
                    {
                        throw new Exception($"方法：{interfaceType.FullName}.{targetMethod.Name}只能设置SelectAttribute或ExecuteAttribute其中一个特性");
                    }
                    if (selectAttribute != null && !hasReturnValue)
                    {
                        throw new Exception($"方法：{interfaceType.FullName}.{targetMethod.Name}设置的是查询特性，但是没有返回值");
                    }

                    //这是个泛型方法
                    if (targetMethod.IsGenericMethod)
                    {
                        //获取所有泛型参数类型定义
                        Type[] Args = targetMethod.GetGenericArguments();
                        List<string> GenericArgNames = new List<string>();
                        for (int j = 0; j < Args.Length; j++)
                        {
                            GenericArgNames.Add(Args[j].Name);
                        }
                        //代理类方法生成泛型参数定义
                        GenericTypeParameterBuilder[] DGP = methodBuilder.DefineGenericParameters(GenericArgNames.ToArray());
                        //泛型参数约束设置
                        for (int j = 0; j < DGP.Length; j++)
                        {
                            //泛型参数继承约束
                            DGP[j].SetBaseTypeConstraint(Args[j].BaseType);
                            //泛型参数完成接口约束
                            DGP[j].SetInterfaceConstraints(Args[j].GetInterfaces());
                        }

                    }

                    //获取需要方法内运行的方法
                    string excuteMethodName = string.Empty;
                    MethodInfo executeMethod = null;

                    if (!hasReturnValue)
                    {
                        excuteMethodName = isTask ? "ExecuteNoReturnAsync" : "ExecuteNoReturn";
                        executeMethod = repositoryType.GetMethod(excuteMethodName);
                        if (executeMethod == null) throw new Exception("找不到执行方法");
                    }
                    else
                    {
                        if (selectAttribute != null)
                        {
                            Type genericType = isAsync ? returnType.GenericTypeArguments.First() : returnType;
                            var baseGenericType = GetUnderlyingType(returnType, isAsync);
                            var isPage = false;//判断是否分页
                            var isIEnumerable = false; //判断是否为集合
                            if (genericType.IsGenericType)
                            {

                                var definitionType = genericType.GetGenericTypeDefinition();
                                isPage = typeof(PagedList<>).IsAssignableFrom(definitionType);
                                isIEnumerable = typeof(List<>).IsAssignableFrom(definitionType)
                                    || typeof(IList<>).IsAssignableFrom(definitionType)
                                    || typeof(IEnumerable<>).IsAssignableFrom(definitionType);
                            }
                            if (isAsync == false)
                            {
                                if (isIEnumerable)
                                {
                                    excuteMethodName = "QueryList";
                                }
                                else if (isPage)
                                {
                                    excuteMethodName = "QueryPaging";
                                }
                                else
                                {
                                    excuteMethodName = "QueryFirst";
                                }
                            }
                            else
                            {
                                var taskType = GetTaskType(returnType);
                                if (isIEnumerable)
                                {
                                    excuteMethodName = "QueryListAsync";
                                }
                                else if (isPage)
                                {
                                    excuteMethodName = "QueryPagingAsync";
                                }
                                else
                                {
                                    excuteMethodName = "QueryFirstAsync";
                                }
                            }

                            var methodTmp = repositoryType.GetMethod(excuteMethodName);
                            if (methodTmp == null) throw new Exception("找不到执行方法");

                            //为泛型方法提供泛型参数
                            executeMethod = methodTmp.MakeGenericMethod(baseGenericType);
                        }
                        else
                        {
                            if (executeAttribute.ReturnId)
                            {
                                excuteMethodName = isAsync ? "ExecuteReturnIdAsync" : "ExecuteReturnId";
                            }
                            else
                            {
                                excuteMethodName = isAsync ? "ExecuteAsync" : "Execute";
                            }
                            executeMethod = repositoryType.GetMethod(excuteMethodName);
                            if (executeMethod == null) throw new Exception("找不到执行方法");
                        }
                    }


                    ////method参数
                    il.Emit(OpCodes.Call,
                           typeof(RepositoryFacotry).GetMethod("get_MethodsCache", BindingFlags.Static | BindingFlags.Public));
                    il.Emit(OpCodes.Ldstr, methodKey);
                    il.Emit(OpCodes.Call, typeof(Dictionary<string, MethodInfo>).GetMethod("get_Item"));

                    //parameters 参数
                    il.Emit(OpCodes.Call, repositoryType.GetMethod("InitParameters"));

                    for (int i = 0; i < parameterType.Length; i++)
                    {

                        il.Emit(OpCodes.Dup);
                        il.Emit(OpCodes.Ldarg_S, i + 1);
                        if (parameterType[i].IsValueType)
                        {
                            il.Emit(OpCodes.Box, parameterType[i]);
                        }
                        il.Emit(OpCodes.Callvirt, typeof(List<object>).GetMethod("Add"));
                    }

                    il.Emit(OpCodes.Call, executeMethod);
                    //返回到方法开始（返回）
                    il.Emit(OpCodes.Ret);

                    typeBuilder.DefineMethodOverride(methodBuilder, targetMethod);
                }
            }

            var resultType = typeBuilder.CreateType();

            return resultType;
        }

        /// <summary>
        /// 获得基础类型，获得比如被Task，ICollection ，IEnumable ,IQueryable 等包裹的类型
        /// </summary>
        /// <param name="returnType"></param>
        /// <param name="isAsync"></param>
        /// <returns></returns>
        private static Type GetUnderlyingType(Type returnType, bool isAsync)
        {
            var resultTmp = isAsync ? GetTaskType(returnType) : returnType;
            var resultTmp2 = resultTmp.IsGenericType
                ? resultTmp.GetGenericArguments().First()
                : resultTmp;

            return resultTmp2;
        }
        /// <summary>
        /// 获取task所包裹的类型
        /// </summary>
        /// <param name="returnType"></param>
        /// <returns></returns>
        private static Type GetTaskType(Type returnType)
        {
            return returnType.GenericTypeArguments.First();
        }
    }
}
